#include "error.h"
#include "lexical.h"
#include <stdio.h>
#include <assert.h>
#include "parser.h"
#include "symbol.h"
#include "sym_table.h"


int warn_num_g = 0; // 警告个数
int err_num_g = 0;  // 错误个数

int show_error_num(void)
{
    if(err_num_g)
        printf(HI_RED);
    else
        printf(HI_CYA);
    
    printf("warning: %d, error: %d\n", warn_num_g, err_num_g);
    printf(CLOSE);
    return err_num_g;
}

/*
    打印词法错误
*/

void lex_err(lex_err_code_e code, err_level_e lvl)
{  
    /* 和 lex_err_code_e 一一对应 */
    const char *lex_err_table[] = {
        "标识符过长",           // ID_2_LONG
        "数字过长",             // NUM_2_LONG
        "字符串过长",           // STR_2_LONG
        "多行注释没有正常结束",  // COMMENT_NO_END
        "字符串缺少右引号",     // STR_INCOMPLT
        "字符不完整",           // CHARA_INCOMPLT
        "逻辑或不完整",         // OR_NO_PAIR
        "无法识别的词法记号",    // UNRECOGNIZED_TOKEN
        "字符常量为空",         // EMPTY_CHARA   
        "未知转义字符",         // UNKNOW_ESCAPE_SEQUENCE
    };

    assert(LEX_ERR_CODE_NUM == ARR_ELEM_CNT(lex_err_table));

#ifndef COLOR_ENABLE
#undef HI_RED
#undef HI_YEL
#undef CLOSE
#define HI_RED
#define HI_YEL
#define CLOSE
#endif
    const char *level_str;
    if(lvl == ERROR) {
        level_str = HI_RED"error:"CLOSE;
        err_num_g++;
    } else {
        level_str = HI_YEL"warning:"CLOSE;
        warn_num_g++; 
    }
        
    const char *file_name = scanner_get_file_name();
    int line = scanner_get_line_num();
    int col = scanner_get_col_num();
    char ch = scanner_get_cur_ch();

    if (code == ID_2_LONG) {
        printf("%s:%d:%d: %s %s, 仅识别标识符：%s\n", 
            file_name, line, col, level_str, lex_err_table[code], scanner_get_token_id());
    } else if (code == STR_2_LONG) {
        printf("%s:%d:%d: %s %s, 仅识别字符串：%s\n", 
            file_name, line, col, level_str, lex_err_table[code], scanner_get_token_str());
    } else if (code == NUM_2_LONG) {
        printf("%s:%d:%d: %s %s, 仅识别数字：%d\n", 
            file_name, line, col, level_str, lex_err_table[code], scanner_get_token_num());
    } else if (code == UNRECOGNIZED_TOKEN) {
        printf("%s:%d:%d: %s %s:\'%c\'(ASCII:%d)\n", file_name, line, col, level_str,
               lex_err_table[code], ch, ch);
    } else {
        printf("%s:%d:%d: %s %s\n", file_name, line, col, level_str, lex_err_table[code]);
    }
}



/*
	打印语法错误
*/
void syn_err(syn_err_code_e code,  err_level_e lvl, int cause, const char *name)
{	
	const char *syn_err_code_tab[]=
	{
		"类型",
        "标识符",
        "(",
        ")",
        "[",
        "]",                 // expected ‘]’ before ‘;’
        "{",                 //   expected ‘{’
        "}",                 //   expected ‘}’
        ",",
        ";",
        "=",
        ":",
        "while",              // expected 'while'
        "数组的元素个数是负数或零",  // error: size of array ‘xx’ is negative
        "缺少表达式或常量",
        "缺少常量",
        "禁止函数嵌套定义", 
        "数组长度必须是常量",
        "case label 必须是整形常量",
        "只能是语句(statement), 不能是声明(declaration)",
        "忽略token ",
        "语句永远不会被执行",// statement will never be executed

	};
    assert(SYN_ERR_CODE_NUM == ARR_ELEM_CNT(syn_err_code_tab));

    const char *level_str;
    if(lvl == ERROR) {
        level_str = HI_RED"error:"CLOSE;
        err_num_g++;
    } else {
        level_str = HI_YEL"warning:"CLOSE;
        warn_num_g++; 
    }
	        
    const char *file_name = scanner_get_file_name();
    int line = scanner_get_line_num();
    int col = scanner_get_col_num();

	if(cause == LOST) {
		printf("%s:%d:%d: %s 在 %s 之前丢失 \'%s\'\n", file_name, line, col, level_str, 
                scanner_get_token_name(), syn_err_code_tab[code]);
    } else if(cause == NOT_MATCH) {
        printf("%s:%d:%d: %s 在 %s 处没有正确匹配 \'%s\'\n", file_name, line, col, level_str, 
                scanner_get_token_name(), syn_err_code_tab[code]);

    } else {
        switch(code) {
            case ARR_SIZE_NEGATIVE_OR_ZERO:
            case ARR_SIZE_NO_CONSTANT:
                printf("%s:%d:%d: %s 数组\'%s\': %s\n", file_name, line, col, level_str, 
                name, syn_err_code_tab[code]);
                break;

            case FORBIDS_NESTED_FUNCTIONS:
                printf("%s:%d:%d: %s 函数\'%s\': %s\n", file_name, line, col, level_str, 
                name, syn_err_code_tab[code]);
                break;
            case EXPECTED_LITERAL_OR_EXPR:
                printf("%s:%d:%d: %s 在\'%s\'之前: %s\n", file_name, line, col, level_str, 
                scanner_get_token_name(), syn_err_code_tab[code]);
                break;

            case CASE_LABEL_NOT_INT_CONSTANT:
            case NEED_STATEMENT:
            case STATEMENT_NEVER_BE_EXECUTED:
                printf("%s:%d:%d: %s %s\n", file_name, line, col, level_str,  syn_err_code_tab[code]);
                break;
 
            case EXPECTED_LITERAL:   
                printf("%s:%d:%d: %s 在\'%s\'之前: %s\n", file_name, line, col, level_str, 
                scanner_get_token_name(), syn_err_code_tab[code]);
                break;
    
            case IGNORE_TOKEN:
                printf("%s:%d:%d: %s %s'%s'\n", file_name, line, col, level_str, syn_err_code_tab[code], name);
                break;
           
            default:
                
                break;
        }
         
    }
	
}


void recovery(bool cond, syn_err_code_e code,  err_level_e lvl)
{
	if(cond)	/* 在给定的集合内 */
		syn_err(code, lvl, LOST, NULL);
	else{
		syn_err(code, lvl, NOT_MATCH, NULL);
		next_token(); // 这才是关键
	}
}





/*
	打印语义错误 semantic
*/ 
void sem_err(sem_err_code_e code,  err_level_e lvl, const char *name, void *addition)
{
	//语义错误信息串
	static const char *sem_err_code_tab[]=
	{
        "变量声明为 void 类型",  // error: variable or field ‘xxxxx’ declared void
        "暂时不支持 void * 类型", // VOID_POINTER_NOT_SUPPORTED
		"数组元素类型不支持指针(目前仅支持 int 和 char)", 
        "数组元素个数为空", //  array size missing
        "变量重定义",	
        "用字符串初始化整形数组", // initialize array of ‘int’ from a string literal
        "初始化字符串超过了数组的长度", //  error: initializer-string for array of ‘char’ is too long
        "数组初始化值过多", // excess elements in array initializer
        "数组初始化元素类型错误",
        "类型冲突",   // conflicting types 
        "extern 修饰函数定义多余",
        "函数重复定义",
        "函数返回类型不支持 void *",
        "函数没有声明", // error: implicit declaration of function ‘foo’ 
        "没有声明", // xxx undeclared
        "作为不同的类型重声明", // ‘xxxx’ redeclared as different kind of symbol；
        "extern 声明与 no linkage 声明冲突",  //error: extern declaration of ‘xxxx’ follows declaration with no linkage
        "只能对左值取地址",
        "暂时只允许对基本类型(char,int)取地址",
        "对基本类型解引用",
        "操作数不能是 void 类型", 
        "不是左值",
        "赋值类型不匹配",
        "比较类型不匹配",
        "操作数类型无效",
        "对一个非数组或非指针类型的变量使用​下标运算符[]",
        "数组下标必须是整形",
        "被调用对象不是函数",
        "实际参数类型错误",
        "暂时不支持对函数做一元操作",
        "用 extern 声明的变量不能带有初始化", // error: ‘a’ has both ‘extern’ and initializer
        "初始化类型错误",  // INITIALIZE_TYPE_INVALID
        "初始化值不是常量", // error: initializer element is not constant
        "return 表达式和函数返回值类型不匹配", // Incompatible return type​
        "表达式不能是 void 类型", 
        "重复的 case 值",  // error: duplicate case value
        "重复的 default 标签", // multiple default labels in one switch
        "break 语句不在循环或 switch 中",  //  break statement not within loop or switch
        "continue 语句不在循环中", // continue statement not within a loop
        "从 int 类型转换到 char 类型时发生了数据溢出",  // overflow in conversion from ‘int’ to ‘char’
        "switch 的参数必须是整形",   //error: switch quantity not an integer
        "暂时不支持函数名",
    };

    assert(SEM_ERR_CODE_NUM == ARR_ELEM_CNT(sem_err_code_tab));

    const char *level_str;
    if(lvl == ERROR) {
        level_str = HI_RED"error:"CLOSE;
        err_num_g++;
    } else {
        level_str = HI_YEL"warning:"CLOSE;
        warn_num_g++; 
    }

    const char *file_name = scanner_get_file_name();
    int line = scanner_get_line_num();
    int col = scanner_get_col_num();


    switch(code) {
        case BREAK_NOT_WITHIN_LOOP_OR_SWITCH:
        case CONTINUE_NOT_WITHIN_LOOP:
        case EXPR_IS_VOID:
            printf("%s:%d:%d: %s %s\n", file_name, line, col, level_str, sem_err_code_tab[code]);
            return;
        case ASSIGN_TYPE_NOT_COMPATIBLE:
            printf("%s:%d:%d: %s %s = %s: %s\n", file_name, line, col, level_str, name, (char *)addition, sem_err_code_tab[code]);
            return;

        case OPERANDS_TYPE_INVALID:
        case COMPARISON_TYPE_NOT_COMPATIBLE:
            if(addition) {
                printf("%s:%d:%d: %s %s 和 %s: %s\n", file_name, line, col, level_str, name, (char *)addition, sem_err_code_tab[code]);
            } else {
                printf("%s:%d:%d: %s %s: %s\n", file_name, line, col, level_str, name, sem_err_code_tab[code]);
            }
            return;

        case CONFLICTING_TYPES:
        case FUNCTION_REDEFINED:
        case REDECLARED_AS_DIFFERENT_KIND_SYMBOL:
        case EXTERN_DECLARATION_AND_NO_LINKAGE_DECLARATION_CONFLICTING:
        case VAR_REDEFINED:
        case DUPLICATE_CASE_VALUE:
            printf("%s:%d:%d: %s '%s' %s,  上一个在第 %d 行.\n", file_name, line, col, level_str, 
                    name, sem_err_code_tab[code], *(int *)addition);
            return;
        case MULTIPLE_DEFAULT_LABELS:
            printf("%s:%d:%d: %s %s,  上一个在第 %d 行.\n", file_name, line, col, level_str, 
                     sem_err_code_tab[code], *(int *)addition);
            return;

        case INCORRECT_TYPE_FOR_FUNCTION_ARGUMENT:
            printf("%s:%d:%d: %s '%s' %s", file_name, line, col, level_str, name, sem_err_code_tab[code]);
            printf(",需要参数：");
            print_fun_para_list((fun_t *)addition, FUN_PARA_LIST_FORMAT_NO_NAME);
            return;
        
    }
	printf("%s:%d:%d: %s '%s' %s\n", file_name, line, col, level_str, name, sem_err_code_tab[code]);
  
}

/*
    编译器内部错误 
    致命错误，会导致程序退出
*/  

void system_fatal_err(system_err_code_e code, const char *file_name, int line) 
{
	static const char *system_err_code_tab[] =
	{	
		"内存分配失败",
		"指针为空",
	};
    assert(SYS_ERR_CODE_NUM == ARR_ELEM_CNT(system_err_code_tab));


	printf("FATAL ERROR! file: %s, line: %d, %s\n",
		file_name, line, system_err_code_tab[code]);
        // TODO
	// 回收内存？
    // exit(); 
}